package com.abel.bigwater.data.sanchuan

import com.abel.bigwater.data.BcdString
import com.abel.bigwater.data.InvalidPacketException
import com.abel.bigwater.data.JsonDateTimeSerializer
import com.fasterxml.jackson.databind.annotation.JsonSerialize
import io.netty.buffer.ByteBuf
import org.joda.time.DateTime

class FetchDataReplyWeekDetail(_dataLen: Int?) : SanchuanData(_dataLen) {
    var cmd: FetchCmdWeekDetail? = null

    var startDateStr: BcdString? = null

    @JsonSerialize(using = JsonDateTimeSerializer::class)
    var startDate: DateTime? = null
        get() = DateTime(startDateStr!!.bcdArray!![0].toInt() * 100 +
                startDateStr!!.bcdArray!![1].toInt(),
                startDateStr!!.bcdArray!![2].toInt(),
                startDateStr!!.bcdArray!![3].toInt(),
                startDateStr!!.bcdArray!![4].toInt(),
                startDateStr!!.bcdArray!![5].toInt())
        set(value) {
            field = value
            startDateStr = if (field == null) null else
                BcdString(byteArrayOf(field!!.year.div(100).toByte(),
                        field!!.year.rem(100).toByte(),
                        field!!.monthOfYear.toByte(),
                        field!!.dayOfMonth.toByte(),
                        field!!.hourOfDay.toByte(),
                        field!!.minuteOfHour.toByte()), true)
        }

    /**
     * TimeInterval（HEX），
    采集点间隔数
    时间间 隔单位的倍
    数， 0F 表示间隔（15*
    时间间隔单位） 分钟
     */
    var timeInterval: Byte? = null

    /**
     * PointNumber（HEX） 05 ， 最 大 不 超 过
    0x30(48)个
     */
    var pointNumber: Byte? = null

    var totalForwardBit: Byte? = null

    /**
     * DecimalBit 1 01 小数位数
    Data1 4 12 34 56 78 16 进制数据， 高位在前，
    示例表示 30541989.6，对于不存在的数据， 用 FF
    FF FF FF 表示
     */
    var forwardLongList: ArrayList<Long>? = null

    var forwardList: ArrayList<Double?>? = null
        get() {
            field = if (forwardLongList == null) null
            else {
                val ratio = Math.pow(10.0, -totalForwardBit!!.toDouble())
                ArrayList<Double?>(forwardLongList!!.size).apply {
                    forwardLongList!!.forEach {
                        add(if (it == 0xFFFFFFFF) null else ratio * it.toDouble())
                    }
                }
            }

            return field
        }


    var flowBit: Byte? = null

    /**
     * DecimalBit 1 01 预留， 不做小数处理
    Data1 4 00 00 09 60 16 进制数据， 高位在前，
    示例表示 2400L/H，
    对于不存在的数据， 用 FF
    FF FF FF 表示
     */
    var flowList: ArrayList<Long>? = null

    var pressureBit: Byte? = null

    /**
     * DecimalBit 1 01 预留， 不做小数处理
    Data1 2 03 25 16 进制数据， 高位在前，
    示例表示 805 KPa，
    对于不存在的数据， 用 FF
    FF 表示
     */
    var pressureIntList: ArrayList<Int>? = null

    var pressureList: ArrayList<Float?>? = null
        get() {
            field = if (pressureIntList == null) null
            else {
                val ratio = Math.pow(10.0, -pressureBit!!.toDouble()).toFloat()
                ArrayList<Float?>(pressureIntList!!.size).apply {
                    pressureIntList!!.forEach {
                        add(if (it == 0xFFFF) null else ratio * it.toFloat())
                    }
                }
            }

            return field
        }


    var revertBit: Byte? = null
    /**
     * Data1 5 12 34 56 78 参数编码 1110
    对于不存在的数据， 用 FF
    FF FF FF FF 表示
     */
    var revertList: ArrayList<BcdString>? = null

    /**
     * 计算数据长度，针对解析和打包
     */
    override fun computeDataLen(): Int {
        if (dataLen == null) dataLen = 1 + 6 + 1 + 1 +
                when (cmd?.dataType) {
                    FetchCmdWeekDetail.DetailDataType.TotalForward -> {
                        // 累计流量据明细
                        if (totalForwardBit == null || forwardLongList == null ||
                                pointNumber!!.toInt() != forwardLongList!!.size) {
                            throw IllegalArgumentException("应包含 ${pointNumber}个点实际只有 ${forwardLongList?.size}个点")
                        }

                        1 + 4 * pointNumber!!.toInt()
                    }

                    FetchCmdWeekDetail.DetailDataType.Flow -> {
                        if (flowBit == null || flowList == null ||
                                flowList!!.size != pointNumber!!.toInt()) {
                            throw IllegalArgumentException("应包含 ${pointNumber}个点实际只有 ${flowList?.size}个点")
                        }

                        1 + 4 * pointNumber!!.toInt()
                    }

                    FetchCmdWeekDetail.DetailDataType.Pressure -> {
                        if (pressureBit == null || pressureIntList == null ||
                                pressureIntList!!.size != pointNumber!!.toInt()) {
                            throw IllegalArgumentException("应包含 ${pointNumber}个点实际只有 ${pressureIntList?.size}个点")
                        }

                        1 + 2 * pointNumber!!.toInt()
                    }

                    FetchCmdWeekDetail.DetailDataType.Revert -> {
                        if (revertBit == null || revertList == null
                                || revertList!!.size != pointNumber!!.toInt()) {
                            throw IllegalArgumentException("应包含 ${pointNumber}个点实际只有 ${revertList?.size}个点")
                        }

                        1 + 5 * pointNumber!!.toInt()
                    }

                    FetchCmdWeekDetail.DetailDataType.AllFour -> {
                        if (totalForwardBit == null || forwardLongList == null ||
                                pointNumber!!.toInt() != forwardLongList!!.size ||
                                flowBit == null || flowList == null ||
                                pressureBit == null || pressureIntList == null ||
                                revertBit == null || revertList == null
                        ) {
                            throw IllegalArgumentException("请正确设置全部数据")
                        }

                        1 + 1 + 1 + 1 + (4 + 4 + 2 + 5) * pointNumber!!.toInt()
                    }

                    else -> {
                        throw IllegalArgumentException("不支持的详细数据类型")
                    }
                }

        return dataLen!!
    }

    /**
     * 从当前位置写入打包字节流
     */
    override fun fillBuffer(buf: ByteBuf) {
        computeDataLen()

        buf.alloc().buffer().apply {
            writeByte(cmd!!.back)

            assert(startDateStr!!.size == 6)
            writeBytes(startDateStr!!.hexArray)

            writeByte(timeInterval!!.toInt())

            writeByte(pointNumber!!.toInt())

            when (cmd?.dataType) {
                FetchCmdWeekDetail.DetailDataType.TotalForward -> {
                    // 累计流量据明细
                    writeByte(totalForwardBit!!.toInt())
                    forwardLongList!!.forEach { writeInt(it.toInt()) }
                }

                FetchCmdWeekDetail.DetailDataType.Flow -> {
                    // 瞬时流量明细
                    writeByte(flowBit!!.toInt())
                    flowList!!.forEach { writeInt(it.toInt()) }
                }

                FetchCmdWeekDetail.DetailDataType.Pressure -> {
                    // 压力数据明细
                    writeByte(pressureBit!!.toInt())
                    pressureIntList!!.forEach { writeShort(it.toInt()) }
                }

                FetchCmdWeekDetail.DetailDataType.Revert -> {
                    // 反向流量明细
                    writeByte(revertBit!!.toInt())
                    revertList!!.forEach { writeBytes(it.hexArray) }
                }

                FetchCmdWeekDetail.DetailDataType.AllFour -> {
                    // 累计流量据明细
                    writeByte(totalForwardBit!!.toInt())
                    forwardLongList!!.forEach { writeInt(it.toInt()) }

                    // 瞬时流量明细
                    writeByte(flowBit!!.toInt())
                    flowList!!.forEach { writeInt(it.toInt()) }

                    // 压力数据明细
                    writeByte(pressureBit!!.toInt())
                    pressureIntList!!.forEach { writeShort(it.toInt()) }

                    // 反向流量明细
                    writeByte(revertBit!!.toInt())
                    revertList!!.forEach {
                        assert(5 == it.size)
                        writeBytes(it.hexArray)
                    }
                }

                else -> {
                    throw IllegalArgumentException("不识别的类型: ${cmd?.dataType}")
                }
            }

            // transfer to buf
            buf.writeBytes(this)
        }
    }

    /**
     * 解析字节流，从buf当前位置开始，并且消费其中的字节。
     */
    override fun <T : SanchuanData> parseBuffer(buf: ByteBuf): T? {
        if (dataLen!! < 1 + 6 + 1 + 1) throw InvalidPacketException("数据不足")

        buf.readBytes(dataLen!!).apply {
            cmd = FetchCmdWeekDetail(readByte().toInt().and(0xFF))

            startDateStr = BcdString.create(this, 6)
            startDate

            timeInterval = readByte()

            pointNumber = readByte()

            when (cmd?.dataType) {
                FetchCmdWeekDetail.DetailDataType.TotalForward -> {
                    // 累计流量据明细
                    totalForwardBit = readByte()

                    forwardLongList = ArrayList(pointNumber!!.toInt())
                    for (idx in 0..(pointNumber!! - 1)) {
                        forwardLongList!!.add(readInt().toLong().and(0xFFFFFFFF))
                    }
                }

                FetchCmdWeekDetail.DetailDataType.Flow -> {
                    // 瞬时流量明细
                    flowBit = readByte()

                    flowList = ArrayList(pointNumber!!.toInt())
                    for (idx in 0..(pointNumber!! - 1)) {
                        flowList!!.add(readInt().toLong().and(0xFFFFFFFF))
                    }
                }

                FetchCmdWeekDetail.DetailDataType.Pressure -> {
                    // 压力数据明细
                    pressureBit = readByte()

                    pressureIntList = ArrayList(pointNumber!!.toInt())
                    for (idx in 0..(pointNumber!! - 1)) {
                        pressureIntList!!.add(readShort().toInt().and(0xFFFF))
                    }
                }

                FetchCmdWeekDetail.DetailDataType.Revert -> {
                    // 反向流量明细
                    revertBit = readByte()

                    revertList = ArrayList(pointNumber!!.toInt())
                    for (idx in 0..(pointNumber!! - 1)) {
                        revertList!!.add(BcdString.create(this, 5))
                    }
                }

                FetchCmdWeekDetail.DetailDataType.AllFour -> {
                    // 累计流量据明细
                    totalForwardBit = readByte()

                    forwardLongList = ArrayList(pointNumber!!.toInt())
                    for (idx in 0..(pointNumber!! - 1)) {
                        forwardLongList!!.add(readInt().toLong().and(0xFFFFFFFF))
                    }

                    // 瞬时流量明细
                    flowBit = readByte()

                    flowList = ArrayList(pointNumber!!.toInt())
                    for (idx in 0..(pointNumber!! - 1)) {
                        flowList!!.add(readInt().toLong().and(0xFFFFFFFF))
                    }

                    // 压力数据明细
                    pressureBit = readByte()

                    pressureIntList = ArrayList(pointNumber!!.toInt())
                    for (idx in 0..(pointNumber!! - 1)) {
                        pressureIntList!!.add(readShort().toInt().and(0xFFFF))
                    }

                    // 反向流量明细
                    revertBit = readByte()

                    revertList = ArrayList(pointNumber!!.toInt())
                    for (idx in 0..(pointNumber!! - 1)) {
                        revertList!!.add(BcdString.create(this, 5))
                    }
                }
            }
        }

        return this as T?
    }
}